# pyright: reportUnreachable=false

from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.db.models import Prefetch
from audit.models import AuditManagement
from rest_framework.permissions import BasePermission
from lib.rest_api.perms import HasDjangoPermission
from rest_framework import generics
from rest_framework.views import APIView, Response, status
from django.db.models.signals import post_save

from .. import models
from . import serializers
from media.models import File
from media.api.serializers import FileSerializer
from user.models import Researcher

User = get_user_model()

class MyInformationView(APIView):
    """
    Return current user information
    """
    def get(self, request, format=None):
        user = request.user
        return Response(serializers.UserSerializer(user).data)

class MyPermissionView(APIView):
    """
    Return current user permission
    """
    def get(self, request, format=None):
        user = request.user
        return Response(serializers.UserPermissionSerializer(user).data)
    

class InviteCodeListView(generics.ListAPIView):
    queryset = models.InviteCode.objects.all()
    serializer_class = serializers.InviteCodeSerializer
    
    def get_permissions(self):
        return [HasDjangoPermission(["user.can_manage_users"])]

class DeleteInviteCodesAPIView(APIView):
    def post(self, request, format=None):
        code_ids = request.data
        
        # Validate input format
        if not isinstance(code_ids, list):
            return Response(
                {"error": "Invalid data format, expected a list of invite codes"}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Check for duplicates
        duplicates = {uuid for uuid in code_ids if code_ids.count(uuid) > 1}
        if duplicates:
            return Response(
                {"error": f"Duplicate invite codes found: {list(duplicates)}"},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Check existence and permissions
        files = models.InviteCode.objects.filter(code__in=code_ids)
        found_uuids = set(map(str, files.values_list('code', flat=True)))
        missing_uuids = set(code_ids) - found_uuids
        
        if missing_uuids:
            return Response(
                {"error": f"Codes not found: {list(missing_uuids)}"},
                status=status.HTTP_404_NOT_FOUND
            )
        
        # Batch delete
        files.delete()
        return Response("ok!", status=status.HTTP_200_OK)

    def get_permissions(self):
        return [HasDjangoPermission(["user.can_manage_users"])]


class UserListView(generics.ListCreateAPIView):
    queryset = (
        models.UserProfile.objects
        .filter(is_active=True)
        .prefetch_related(
            'user_permissions', 
            'groups',
            Prefetch(
                'audit_management',
                queryset=AuditManagement.objects.order_by('-updated_at'),
                to_attr='_audit_management'
            )
        )
    )
    
    def get_serializer_class(self): # pyright:ignore [ reportIncompatibleMethodOverride ]
        return (
            serializers.UserUpdateSerializer 
            if self.request.method == 'POST' 
            else serializers.UserSerializer
        )

    def get_permissions(self):
        return [HasDjangoPermission(["user.can_manage_users"])]
    
    def post(self, request, *args, **kwargs):
        if not isinstance(request.data, list):
            return Response(
                {"error": "Expected a list of user data"},
                status=status.HTTP_400_BAD_REQUEST
            )

        
        serializer = self.get_serializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        
        # Bulk update implementation
        user_ids = [item['id'] for item in serializer.validated_data]
        users = models.UserProfile.objects.filter(id__in=user_ids)
        user_map = {user.id: user for user in users}

        # Validate all users exist
        missing_ids = [uid for uid in user_ids if uid not in user_map]
        if missing_ids:
            return Response(
                {"error": f"Users not found: {missing_ids}"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Update fields
        fields_to_update = set()
        for data in serializer.validated_data:
            user = user_map[data['id']]
            for key, value in data.items():
                if key != 'id':
                    setattr(user, key, value)
                    fields_to_update.add(key)

        # Perform bulk update (write to db)
        if fields_to_update:
            models.UserProfile.objects.bulk_update(
                users, 
                fields=fields_to_update
            )

        # manually send signals since bulk update doesn't send post_save signals
        for user in users:
            post_save.send(models.UserProfile, instance=user, created=False)


        response_serializer = serializers.UserSerializer(
            users, 
            many=True,
            context=self.get_serializer_context()
        )
        return Response(response_serializer.data, status=status.HTTP_200_OK)
    

class ChangeUserPermissionView(generics.ListAPIView):
    serializer_class = serializers.UserPermissionSerializer
    queryset = User.objects.all()
    
    # FIXME user with can_manage_users shouldn't be able to change its own permission (should be changed
    # by superuser)
    def post(self, request, format=None):
        serializer = self.serializer_class(data=request.data, many=True)
        # don't call serializer.is_valid(), since it checks all values like a form
        perm_can_manage_projects = Permission.objects.get(codename='can_manage_projects')
        perm_can_manage_users = Permission.objects.get(codename='can_manage_users')
        perm_can_manage_resources = Permission.objects.get(codename='can_manage_resources')
        internal_staff_group = Group.objects.get(name='internal_staff')
        external_personal_group = Group.objects.get(name='external_personal')
       
        for user_data in serializer.initial_data:
            user = User.objects.get(id=user_data['id'])
            # update fields

            user.is_staff = user_data['is_staff']

            if user_data['perm_can_manage_projects']:
                user.user_permissions.add(perm_can_manage_projects)
            else:
                user.user_permissions.remove(perm_can_manage_projects)
                
            if user_data['perm_can_manage_users']:
                user.user_permissions.add(perm_can_manage_users)
            else:
                user.user_permissions.remove(perm_can_manage_users)

            if user_data['perm_can_manage_resources']:
                user.user_permissions.add(perm_can_manage_resources)
            else:
                user.user_permissions.remove(perm_can_manage_resources)

            if user_data['user_group_type'] == 'internal_staff':
                if user.groups.filter(name='external_personal').exists():
                    user.groups.remove(external_personal_group)
                user.groups.add(internal_staff_group)
            elif user_data['user_group_type'] == 'external_personal':
                if user.groups.filter(name='internal_staff').exists():
                    user.groups.remove(internal_staff_group)
                user.groups.add(external_personal_group)
            else:
                return Response(
                    f"user_group_type cannot be '{user_data['user_group_type']}'!",
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            user.save()
                
        return Response(serializer.initial_data, status=status.HTTP_200_OK)

    def get_permissions(self):
        return [HasDjangoPermission(["user.can_manage_users"])]

    
class UserIsResearcherPermission(BasePermission):
    def has_permission(self, request, view):
        user = request.user
        return user.is_researcher

class SettingResumeListAPIView(generics.ListAPIView):
    serializer_class = FileSerializer
    permission_classes = [UserIsResearcherPermission]

    def get_queryset(self):
        user = self.request.user
        researcher = user.researcher_profile
        return File.objects.get_for_object(researcher, "resume")

class ResearchersResumesAPIView(generics.ListAPIView):
    serializer_class = serializers.ResearchersResumesSerializer

    def get_queryset(self):
        # drf calls `get_queryset` twice
        if getattr(self, "_files", None):
            return getattr(self, "_files")
        
        researcher_ct = ContentType.objects.get_for_model(Researcher)
        files = File.objects.select_related("owner").filter(
            object_content_type=researcher_ct,
            relation_name='resume'
        )
                
        # Extract all researcher IDs
        researcher_ids = [int(file.object_id) for file in files]
        
        # Fetch all relevant researchers with their users in a single query
        researchers = {
            r.id: r for r in Researcher.objects.filter(
                id__in=researcher_ids
            ).select_related('user')
        }
        
        # Attach researchers to the files to avoid additional queries
        for file in files:
            researcher_id = int(file.object_id)
            if researcher_id in researchers:
                # Store the researcher object with the file to avoid additional lookups
                file._prefetched_researcher = researchers[researcher_id]
                
        setattr(self, "_files", files)
        
        return files
    
    def get_permissions(self):
        return [HasDjangoPermission(["user.can_manage_users"])]
